package config

import (
	"smart-flow/monitor/logger"
	"strings"
	"sync"

	"github.com/fsnotify/fsnotify"
	"go.uber.org/zap"

	"github.com/spf13/viper"
)

type MonitorConfig struct {
	Mode          string `mapstructure:"mode"`
	InterfaceName string `mapstructure:"interface_name"`
	Isp           string `mapstructure:"isp"`
	ProverV6      string `mapstructure:"prover_v6"`
	Capture       int    `mapstructure:"capture"`
	MachineIdFile string
}

type LogConfig struct {
	LogPath string `mapstructure:"path"`
}

type StatConfig struct {
	Ips []string `mapstructure:"ips"`
}

type DyConfig struct {
	Enable           int    `mapstructure:"enable"`
	PusherInterval   int64  `mapstructure:"pusher_interval"`
	PusherProportion int    `mapstructure:"pusher_proportion"`
	StatInterval     int64  `mapstructure:"stat_interval"`
	PusherStrictRate int    `mapstructure:"pusher_strict_rate"`
	ExpiredLimit     int    `mapstructure:"expired_limit"`
	ServerUrl        string `mapstructure:"server_url"`
	ServerInterval   int64  `mapstructure:"server_interval"`
	AgentMaxEndpoint int    `mapstructure:"agent_max_endpoint"`
	AgentInterval    int64  `mapstructure:"agent_interval"`
	MaxDownV4        int64  `mapstructure:"max_down_v4"`
	MaxDownV6        int64  `mapstructure:"max_down_v6"`
	MaxDownTotalV4   int64  `mapstructure:"max_down_total_v4"`
	MaxDownTotalV6   int64  `mapstructure:"max_down_total_v6"`
	MaxDownTotal     int64  `mapstructure:"max_down_total"`
	TcpProportion    int64  `mapstructure:"tcp_proportion"`
	UdpProportion    int64  `mapstructure:"udp_proportion"`
	HttpProportion   int64  `mapstructure:"http_proportion"`
	DeltaProportion  int64  `mapstructure:"delta_proportion"`
}

type Config struct {
	Monitor MonitorConfig `mapstructure:"monitor"`
	Log     LogConfig     `mapstructure:"log"`
	Stat    StatConfig    `mapstructure:"stat"`
	Dy      DyConfig      `mapstructure:"dy"`
}

var (
	configPath string
	config     *Config
	mu         sync.RWMutex
)

func AppConfig() *Config {
	mu.RLock()
	defer mu.RUnlock()
	return config
}

func LoadConfig(path string) {
	defaultConfig()
	configPath = path
	viper.AddConfigPath(path)
	viper.SetConfigName("monitor")
	viper.SetConfigType("yaml")
	err := viper.ReadInConfig()
	if err != nil {
		panic(err.Error())
	}
	err = viper.Unmarshal(&config)
	if err != nil {
		panic(err.Error())
	}
	config.Monitor.MachineIdFile = "machine_id"
	validConfig()
	watcher, _ := fsnotify.NewWatcher()
	err = watcher.Add(configPath + "/monitor.yaml")
	if err != nil {
		panic(err.Error())
	}
	go func() {
		for event := range watcher.Events {
			if event.Op&fsnotify.Write == fsnotify.Write {
				reloadConfig()
			}
		}
	}()
}

func reloadConfig() {
	mu.Lock()
	defer mu.Unlock()
	viper.AddConfigPath(configPath)
	viper.SetConfigName("monitor")
	viper.SetConfigType("yaml")
	err := viper.ReadInConfig()
	if err != nil {
		logger.Logger.Error("[系统] 更新配置失败", zap.Error(err))
		return
	}
	var dy DyConfig
	err = viper.UnmarshalKey("dy", &dy)
	if err != nil {
		logger.Logger.Error("[系统] 更新配置失败", zap.Error(err))
	}
	config.Dy = dy
	logger.Logger.Info("[系统] 更新配置成功", zap.Any("dy_config", config.Dy))
}

func defaultConfig() {
	viper.SetDefault("monitor.mode", "debug")
	viper.SetDefault("log.path", "./log")
	viper.SetDefault("stat.ips", []string{})
	viper.SetDefault("dy.pusher_interval", 3600)
	viper.SetDefault("dy.pusher_proportion", 10000)
	viper.SetDefault("dy.stat_interval", 300)
	viper.SetDefault("dy.server_interval", 30)
	viper.SetDefault("dy.agent_max_endpoint", 200)
	viper.SetDefault("dy.agent_interval", 300)
	viper.SetDefault("dy.delta_proportion", RateDelta)
}

func validConfig() {
	if config.Monitor.Mode != "debug" && config.Monitor.Mode != "test" && config.Monitor.Mode != "release" {
		panic("server mode must be debug, test or release")
	}
	if strings.TrimSpace(config.Monitor.InterfaceName) == "" {
		panic("interface name is empty")
	}
	if strings.TrimSpace(config.Monitor.Isp) == "" {
		panic("isp is empty")
	}
	if strings.TrimSpace(config.Dy.ServerUrl) == "" {
		panic("server url is empty")
	}
	if config.Dy.ServerInterval < 1 {
		panic("server interval must be greater than 0")
	}
	if config.Dy.AgentMaxEndpoint < 1 {
		panic("agent max endpoint must be greater than 0")
	}
	if config.Dy.AgentInterval < 1 {
		panic("agent interval must be greater than 0")
	}
	if config.Dy.MaxDownV4 < 1 && config.Dy.MaxDownV6 < 1 {
		panic("max down v4 or v6 must be greater than 0")
	}
	if config.Dy.MaxDownTotalV4 < 1 && config.Dy.MaxDownTotalV6 < 1 {
		panic("max down total v4 or v6 must be greater than 0")
	}
	if config.Dy.MaxDownTotal < 1 {
		panic("max down total must be greater than 0")
	}
	if config.Dy.TcpProportion < 1 && config.Dy.UdpProportion < 1 && config.Dy.HttpProportion < 1 {
		panic("tcp or udp or http proportion must be greater than or eq 0")
	}
	if config.Dy.DeltaProportion < 0 || config.Dy.DeltaProportion > 50 {
		panic("delta proportion must be eq 0-50")
	}
	if config.Dy.Enable != 0 && config.Dy.Enable != 1 {
		panic("dy enable must be 0 or 1")
	}
	if config.Dy.PusherInterval < 1 {
		panic("pusher interval must be greater than 0")
	}
	if config.Dy.PusherProportion < 1 {
		panic("pusher proportion must be greater than 0")
	}
	if config.Dy.StatInterval < 1 {
		panic("stat interval must be greater than 0")
	}
	if config.Dy.PusherStrictRate < 0 {
		panic("pusher high speed must be greater than 0")
	}
	if config.Dy.ExpiredLimit < 0 {
		panic("expired limit must be greater than 0")
	}
}
